package snuvm.tools;

import snuvm.*;
import snuvm.arm9.hardware.*;
import snuvm.venus.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;

/*
Copyright (c) 2009-2010,  Jeongho Nah, Gangwon Jo, Jaejin Lee

Developed by 
	Jeongho Nah (jeongho"at"aces.snu.ac.kr)
	Gangwon Jo (gangwon"at"aces.snu.ac.kr)
	Jaejin Lee (jlee"at"cse.snu.ac.kr)

Permission is hereby granted, free of charge, to any person obtaining 
a copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject 
to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(For more details, http://www.opensource.org/licenses/mit-license.html)


"MARS Copyright (c) 2003-2009,  Pete Sanderson and Kenneth Vollmar"
 */

/**
 * Simple Demo of Mars tool capability
 */

    public class MarsBot implements Observer, MarsTool
   {
      private static final int GRAPHIC_WIDTH = 512;
      private static final int GRAPHIC_HEIGHT = 512;
      private static final int ADDR_HEADING = 0xffff8010;
      private static final int ADDR_LEAVETRACK = 0xffff8020;
      private static final int ADDR_WHEREAREWEX = 0xffff8030;
      private static final int ADDR_WHEREAREWEY = 0xffff8040;
      private static final int ADDR_MOVE = 0xffff8050;
      private MarsBotDisplay graphicArea;
      private int MarsBotHeading = 0; // 0 --> North (up), 90 --> East (right), etc.
      private boolean MarsBotLeaveTrack = false; // true --> leave track when moving, false --> do not ...
      private double MarsBotXPosition = 0; // X pixel position of MarsBot
      private double MarsBotYPosition = 0; // Y pixel position of MarsBot
      private boolean MarsBotMoving = false; // true --> MarsBot is moving, false --> MarsBot not moving
   
    // The begin and end points of a "track" segment are kept in neighboring pairs
    // of elements of the array. arrayOfTrack[i] is the start pt, arrayOfTrack[i+1] is
    // the end point of a path that should leave a track.
      private final int trackPts = 256;  // TBD Hardcoded. Array contains start-end points for segments in track.
      private Point[] arrayOfTrack = new Point[trackPts];
      private int trackIndex = 0;
   
    // private inner class
       private class BotRunnable implements Runnable
      {
         JPanel panel;
          public BotRunnable() // constructor
         {
            final JFrame frame = new JFrame("Bot");
            panel = new JPanel(new BorderLayout());
            graphicArea = new MarsBotDisplay(GRAPHIC_WIDTH, GRAPHIC_HEIGHT);
            JPanel buttonPanel = new JPanel();
            JButton clearButton = new JButton("Clear");
            clearButton.addActionListener(
                   new ActionListener()
                  {
                      public void actionPerformed(ActionEvent e)
                     {
                        graphicArea.clear();
                        MarsBotLeaveTrack = false; // true --> leave track when moving, false --> do not ...
                        MarsBotXPosition = 0; // X pixel position of MarsBot
                        MarsBotYPosition = 0; // Y pixel position of MarsBot
                        MarsBotMoving = false; // true --> MarsBot is moving, false --> MarsBot not moving
                     
                        trackIndex = 0;
                     
                     }
                  
                  });
            buttonPanel.add(clearButton);
            JButton closeButton = new JButton("Close");
            closeButton.addActionListener(
                   new ActionListener()
                  {
                      public void actionPerformed(ActionEvent e)
                     {
                        frame.setVisible(false);
                     
                     }
                  
                  });
            buttonPanel.add(closeButton);
            panel.add(graphicArea, BorderLayout.CENTER);
            panel.add(buttonPanel, BorderLayout.SOUTH);
            frame.getContentPane().add(panel);
            frame.pack();
            frame.setVisible(true);
            frame.setTitle(" This is the MarsBot");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // TBD --- This should just close the Robot Tool, not the entire MARS
            frame.setSize(GRAPHIC_WIDTH + 200, GRAPHIC_HEIGHT + 100); // TBD  SIZE
            frame.setVisible(true); // show();
         
         } // end BotRunnable() constructor
      
          public void run()
         {
         
            double tempAngle;
            // infinite loop: move the bot according to the current directives
            // (which may be to NOT move)
            do
            {
               if (MarsBotMoving)
               {
                    //System.out.println("BotRunnable.run: bot IS moving.");
                    // TBD This is an arbitrary distance for bot movement. This could just
                    // as easily be a random distance to simulate terrain, etc.
                    // adjust bot position.
                    // The "mathematical angle" is zero at east, 90 at north, etc.
                    // The "heading" is 0 at north, 90 at east, etc.
                    // Conversion: MathAngle = [(360 - heading) + 90] mod 360
                  tempAngle = ((360 - MarsBotHeading) + 90) % 360;
                  MarsBotXPosition += Math.cos(Math.toRadians(tempAngle)); // Math.cos parameter unit is radians
                  MarsBotYPosition += -Math.sin(Math.toRadians(tempAngle)); // Negate value because Y coord grows down
               
                    // Write this new information to MARS memory area
                  try
                  {
                     Globals.memory.setWord(ADDR_WHEREAREWEX, (int) MarsBotXPosition);
                     Globals.memory.setWord(ADDR_WHEREAREWEY, (int) MarsBotYPosition);
                  
                  }
                      catch ( AddressErrorException e)
                     {
                        // TBD TBD TBD No action
                     }
                    
                    //System.out.println(" ------- Heading is " + MarsBotHeading + ", angle is " + tempAngle);
                    //System.out.println(" ------- New X,Y is (" + MarsBotXPosition + "," + MarsBotYPosition + ")" );
               
                    // Whether or not we're leaving a track, write the current point to the
                    // current position in the array.
                    //   -- If we are not leaving a track now, we will need the current point to
                    //      start a future track, and that goes into the array.
                    //   -- If we are leaving a track now, the current point may end the track,
                    //      and that goes into the array.
                  arrayOfTrack[trackIndex] = new Point((int)MarsBotXPosition, (int)MarsBotYPosition);
               
               } 
               else
               {
                    // Action for if the MarsBot isn't moving
                    // System.out.println("BotRunnable.run: bot is not moving.");
               }
            
                // TBD Pause whether the bot is or is not moving. This gives the MIPS program
                // opportunity to consider results of movement, or to make the bot move.
                // ??? What is relationship of robot speed to MARS's
                // execution time for a single instruction? Does the robot speed have to
                // be slow enough to allow a MARS busy loop to detect the bot position
                // at a specific pixel?
               try
               {
                    //System.out.println(" Hello from the bot runner");
                  Thread.sleep(40);
               }
                   catch (InterruptedException exception)
                  {// no action
                  }
                
               panel.repaint(); // show new bot position
            } while (true);
         
         } // end run method of BotRunnable class
      
      } // end BotRunnable class
   
    /* ------------------------------------------------------------------------- */
       private class MarsBotDisplay extends JPanel
      {
         private int width;
         private int height;
         private boolean clearTheDisplay = true;
        
      
          public MarsBotDisplay(int tw, int th)
         {
            width = tw;
            height = th;
         
         }
      
          public void redraw()
         {
            repaint();
         }
      
          public void clear()
         {
            // clear the graphic display
            clearTheDisplay = true;
            //System.out.println("MarsBotDisplay.clear: called to clear the display");
            repaint();
         }
      
          public void paintComponent(Graphics g)
         {
            long tempN;
            // System.out.println("MarsBotDisplay.paintComponent: I'm painting! n is " + n);
         
         
            // Recover Graphics2D
            Graphics2D g2 = (Graphics2D) g;
            
            /*
            if (clearTheDisplay)
            {
                g2.setColor(Color.lightGray);
                g2.fillRect(0, 0, width - 1, height - 1); // Clear all previous drawn information
                clearTheDisplay = false;
            }
            */
         
            // Draw the track left behind, for each segment of the path
            g2.setColor(Color.blue);
            for (int i = 1; i <= trackIndex; i += 2) // Index grows by two (begin-end pair)
            {
               //System.out.print(".");
               try
               {
                  g2.drawLine((int)arrayOfTrack[i-1].getX(), (int)arrayOfTrack[i-1].getY(),
                             (int)arrayOfTrack[i].getX(), (int)arrayOfTrack[i].getY() );
               }
                   catch (ArrayIndexOutOfBoundsException e)
                  {
                  // No action   TBD sloppy
                  }
                   catch (NullPointerException e)
                  {
                  // No action   TBD sloppy
                  }
            }
         
            g2.setColor(Color.black);
            g2.fillRect((int) MarsBotXPosition, (int) MarsBotYPosition, 20, 20); // Draw bot at its current position
         
            /*
             g2.setColor(Color.blue);
             g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), 20) );  // same font and style in larger size
             g2.drawOval( width/2 - 30,  // TBD Hardcoded oval size
             height/2 - 30,
             60,
             60);
             g2.drawString(" " + n, width/2, height/2);
             */
         
         
         }
      
      } // end private inner class MarsBotDisplay
   
    /* ------------------------------------------------------------------------- */
   
   
       public String getName()
      {
         return "Mars Bot";
      }
   
    /*
     * This will set up the Bot's GUI.  Invoked when Bot menu item selected.
     */
       public void action()
      {
         BotRunnable br1 = new BotRunnable();
         Thread t1 = new Thread(br1);
         t1.start();
        // New: DPS 27 Feb 2006.  Register observer for memory subrange.
         try {
            Globals.memory.addObserver(this,0xffff8000,0xffff8060);
         } 
             catch (AddressErrorException aee) { 
               System.out.println(aee);
            } 
      }
   
    /*
     * This method observes MIPS program directives to modify Bot activity (that is,
     * MIPS program write to MMIO) and updates instance variables to reflect that
     * directive.
     */
       public void update(Observable o, Object arg)
      {
         MemoryAccessNotice notice;
         int address;
         if (arg instanceof MemoryAccessNotice)
         {
            notice = (MemoryAccessNotice) arg;
            address = notice.getAddress();
            if (address < 0 && notice.getAccessType() == AccessNotice.WRITE)
            {
               String message = "";
               if (address == ADDR_HEADING)
               {
                  message = "MarsBot.update: got move heading value: ";
                  MarsBotHeading = notice.getValue();
                    //System.out.println(message + notice.getValue() );
               }
               else if (address == ADDR_LEAVETRACK)
               {
                  message = "MarsBot.update: got leave track directive value ";
                    
                    // If we HAD NOT been leaving a track, but we should NOW leave
                    // a track, put start point into array.
                  if (MarsBotLeaveTrack == false && notice.getValue() == 1)
                  {
                     MarsBotLeaveTrack = true;
                     arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition);
                     trackIndex++;  // the index of the end point
                  }
                    // If we HAD NOT been leaving a track, and get another directive
                    // to NOT leave a track, do nothing (nothing to do).
                  else if (MarsBotLeaveTrack == false && notice.getValue() == 0)
                  {
                      // NO ACTION
                  }
                    // If we HAD been leaving a track, and get another directive
                    // to LEAVE a track, do nothing (nothing to do).
                  else if (MarsBotLeaveTrack == true && notice.getValue() == 1)
                  {
                      // NO ACTION
                  }
                    // If we HAD been leaving a track, and get another directive
                    // to NOT leave a track, put end point into array.
                  else if (MarsBotLeaveTrack == true && notice.getValue() == 0)
                  {
                     MarsBotLeaveTrack = false;
                     arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition);
                     trackIndex++;  // the index of the next start point
                  }
               
                    //System.out.println("MarsBotDisplay.paintComponent: putting point in track array at " + trackIndex);
               
                    //System.out.println(message + notice.getValue() );
               }
               else if (address == ADDR_MOVE)
               {
                  message = "MarsBot.update: got move control value: ";
                  if (notice.getValue() == 0) MarsBotMoving = false;
                  else MarsBotMoving = true;
                    //System.out.println(message + notice.getValue() );
               }
               else if (address == ADDR_WHEREAREWEX ||
                         address == ADDR_WHEREAREWEY)
               {
                  // Ignore these memory writes, because the writes originated within
                  // this tool. This tool is being notified of the writes in the usual
                  // manner, but the writes are already known to this tool.
                  // NO ACTION
               }
               else
               {
                    //message = "MarsBot.update: HEY!!! unknown address of " + Integer.toString(address) + ", value: ";
                    //System.out.println(message + notice.getValue() );
               }
            
            }
         }
      
      }
   
   }

